/**
 * Class: FileUtils
 *
 * Purpose: File access shortcuts
 *
 * @author Ken Scott
 */
package org.openfantasia.util;

import javax.swing.*;
import javax.swing.filechooser.FileSystemView;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.rtf.RTFEditorKit;
import java.io.*;
import java.util.*;

public class FileUtils {

	public static File createEmptyFile(File file) throws IOException {
		if (file.exists()) {
			file.delete();
		}
		else {
			file.getParentFile().mkdirs();
		}
		file.createNewFile();

		return file;
	}


	public static File createDirectory(File dir) throws IOException {
		return createDirectory(dir, false);
	}


	public static File createDirectory(File dir, boolean createEmpty) throws IOException {
		if (dir.exists() && createEmpty) {
			deleteDirectory(dir);
		}
		dir.mkdirs();

		return dir;
	}


	public static File deleteDirectory(File dir) throws IOException {
		if (dir.exists() && dir.isDirectory()) {
			File[] files = dir.listFiles();
			for (File file : files) {
				if (file.isDirectory()) {
					deleteDirectory(file);
				}
				else {
					file.delete();
				}
			}
			dir.delete();
		}

		return dir;
	}


	public static String createLocalFilePath(String localPathNoLeadingSlash) {
		return Constants.WORKING_DIR + Constants.FILE_SEPARATOR + localPathNoLeadingSlash;
	}


	public static String createHomeFilePath(String localPathNoLeadingSlash) {
		return Constants.HOME_DIR + Constants.FILE_SEPARATOR + localPathNoLeadingSlash;
	}


	public static File createLocalFile(String localPathNoLeadingSlash) {
		return new File(createLocalFilePath(localPathNoLeadingSlash));
	}


	public static File createHomeFile(String localPathNoLeadingSlash) {
		return new File(createHomeFilePath(localPathNoLeadingSlash));
	}


	public static File createHomeFile(String... paths) {
		return new File(createHomeFilePath(paths));
	}


	public static String createDirectFilePath(String... paths) {
		return createDirectFilePath(Arrays.asList(paths));
	}


	public static File createDirectFile(String... paths) {
		return createDirectFile(Arrays.asList(paths));
	}


	public static String createDirectFilePath(List<String> paths) {
		return TextUtils.detokenize(paths, Constants.FILE_SEPARATOR);
	}


	public static File createDirectFile(List<String> paths) {
		return new File(createDirectFilePath(paths));
	}


	public static String createLocalFilePath(String... paths) {
		return createLocalFilePath(Arrays.asList(paths));
	}


	public static String createHomeFilePath(String... paths) {
		return createHomeFilePath(Arrays.asList(paths));
	}


	public static File createLocalFile(String... paths) {
		return createLocalFile(Arrays.asList(paths));
	}


	public static String createLocalFilePath(List<String> paths) {
		return Constants.WORKING_DIR + Constants.FILE_SEPARATOR + TextUtils.detokenize(paths, Constants.FILE_SEPARATOR);
	}


	public static String createHomeFilePath(List<String> paths) {
		return Constants.HOME_DIR + Constants.FILE_SEPARATOR + TextUtils.detokenize(paths, Constants.FILE_SEPARATOR);
	}


	public static File createLocalFile(List<String> paths) {
		return new File(createLocalFilePath(paths));
	}


	public static List<String> readLinesFromFile(File file) throws IOException {
		List<String> result = new ArrayList<String>();

		if (file.exists()) {
			int len = (int) file.length();
			if (len > 0) {
				FileReader fr = new FileReader(file);
				LineNumberReader lr = new LineNumberReader(fr, len);
				String line = lr.readLine();
				while (line != null) {
					result.add(line);
					line = lr.readLine();
				}
				lr.close();
			}
		}

		return result;
	}


	public static List<String> readLinesFromReader(Reader reader) throws IOException {
		List<String> result = new ArrayList<String>();

		LineNumberReader lr = new LineNumberReader(reader);
		String line = lr.readLine();
		while (line != null) {
			result.add(line);
			line = lr.readLine();
		}
		lr.close();

		return result;
	}


	public static List<String> readLinesFromStream(InputStream inputStream) throws IOException {
		return readLinesFromReader(new InputStreamReader(inputStream));
	}


	public static Iterator<String> iterateLinesFromFile(File file) throws IOException {
		return readLinesFromFile(file).iterator();
	}


	public static void writeObjectToFile(File file, Object object) throws IOException {
		ObjectOutputStream oos = null;
		try {
			createEmptyFile(file);

			oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file), 2 ^ 16));
			oos.writeObject(object);
			oos.flush();
			oos.close();
			oos = null;
		}
		finally {
			if (oos != null) {
				try {
					oos.close();
				}
				catch (IOException x) {
					// ignore
				}
			}
		}
	}


	public static Object readObjectFromFile(File file) throws IOException, ClassNotFoundException {
		Object result = null;

		if (file.exists()) {
			ObjectInputStream ois = null;
			try {
				ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file), 2 ^ 16));
				result = ois.readObject();
				ois.close();
				ois = null;
			}
			finally {
				if (ois != null) {
					try {
						ois.close();
					}
					catch (IOException e) {
						// ignore
					}
				}
			}
		}

		return result;
	}


	public static String readTextFromFile(File file) throws IOException {
		String result = null;

		if (file.exists()) {
			String lcPath = file.getAbsolutePath().toLowerCase();
			if (lcPath.endsWith("rtf")) {
				// RTF File
				try {
					FileInputStream stream = new FileInputStream(file);
					RTFEditorKit kit = new RTFEditorKit();
					Document doc = kit.createDefaultDocument();
					kit.read(stream, doc, 0);

					result = doc.getText(0, doc.getLength());
					stream.close();
				}
				catch (BadLocationException e) {
					e.printStackTrace();
				}
			}
			else {
				// PLAIN
				int len = (int) file.length();
				StringBuilder sb = new StringBuilder(len);
				FileReader fr = new FileReader(file);
				LineNumberReader lr = new LineNumberReader(fr, len);
				String line = lr.readLine();
				while (line != null) {
					sb.append(line);
					sb.append("\n");
					line = lr.readLine();
				}
				lr.close();
				result = sb.toString();
			}
		}

		return result;
	}


	public static String readTextFromStream(InputStream stream) throws IOException {
		StringBuilder result = new StringBuilder();

		LineNumberReader lr = new LineNumberReader(new InputStreamReader(stream));
		String line = lr.readLine();
		while (line != null) {
			result.append(line);
			result.append("\n");
			line = lr.readLine();
		}
		lr.close();

		return result.toString();
	}


	public static String readTextFromFile(String path) throws IOException {
		return readTextFromFile(new File(path));
	}


	public static void saveMapToTextFile(File file, Map<String, String> textMap) throws IOException {
		StringBuilder sb = new StringBuilder();
		for (String key : textMap.keySet()) {
			sb.append(key).append("\t").append(textMap.get(key)).append("\n");
		}

		saveTextToFile(file, sb.toString());
	}


	public static Map<String, String> readMapFromTextFile(File file) throws IOException {
		List<String> lines = readLinesFromFile(file);
		lines = (List<String>) Utils.removeDuplicates(lines);
		lines = Utils.removeCommentBlocks(lines);
		lines = Utils.removeCommentLines(lines);
		lines = Utils.removeEmptyLines(lines);

		Map<String, String> result = new HashMap<String, String>(lines.size());
		for (String line : lines) {
			String[] tokens = line.split("\\t");
			result.put(tokens[0], tokens[1]);
		}

		return result;
	}


	public static void saveTextToFile(File file, Object text) throws IOException {
		createEmptyFile(file);

		PrintWriter writer = new PrintWriter(new FileWriter(file, false));
		writer.print(text != null ? text.toString() : "NULL");
		writer.close();
	}


	public static void saveTextToFile(String path, Object text) throws IOException {
		saveTextToFile(new File(path), text);
	}


	public static void appendTextToFile(File file, Object text) {
		try {
			if (!file.exists()) {
				file.getParentFile().mkdirs();
				file.createNewFile();
			}

			PrintWriter writer = new PrintWriter(new FileWriter(file, true));
			writer.print(text != null ? text.toString() : "NULL");
			writer.close();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}


	public static void appendTextToFile(String path, Object text) {
		appendTextToFile(new File(path), text);
	}


	public static String createValidFilename(String name) {
		return TextUtils.clip(TextUtils.condense(name, Constants.SEPARATORS, "_"), 80);
	}


	public static List<File> getFiles(File directory, boolean includeSubdirectories) {
		List<File> result = new ArrayList<File>();

		File[] files = directory.listFiles(new FileFilter() {
			public boolean accept(File file) {
				return file.isFile();
			}
		});
		result.addAll(Arrays.asList(files));

		if (includeSubdirectories) {
			File[] subDirectories = directory.listFiles(new FileFilter() {
				public boolean accept(File file) {
					return file.isDirectory();
				}
			});

			for (File subDirectory : subDirectories) {
				result.addAll(getFiles(subDirectory, includeSubdirectories));
			}
		}

		return result;
	}


	public static String getFileType(String filename) {
		String result = "";
		int lastDot = filename.lastIndexOf(".");
		if (lastDot > -1) {
			result = filename.substring(lastDot + 1);
		}
		return result;
	}


	public static File openFile(File dir) {
		return openFile(dir, null);
	}


	public static File openFile(File dir, javax.swing.filechooser.FileFilter filter) {
		File result = null;

		JFileChooser chooser = new JFileChooser(dir, FileSystemView.getFileSystemView());
		if (filter != null) {
			chooser.setFileFilter(filter);
		}
		int selection = chooser.showOpenDialog(null);
		if (selection == JFileChooser.APPROVE_OPTION) {
			result = chooser.getSelectedFile();
		}

		return result;
	}
}
